/**!
*   \class Stack
*   \brief Realization of stack
*
*   \author Kharlamov Dmitry
*/

#ifndef STACK_H_INCLUDED
#define STACK_H_INCLUDED

#include <stdlib.h>
#include <exception>
#include <stdexcept>
#include <iostream>

template <typename StElem>
class Stack
{
private:
 const unsigned     MAX_STACK;
       unsigned     len;
        StElem*     data;
       unsigned     counter;
            int     resize_();

public:
        Stack();
        Stack(unsigned length);

        ~Stack();

        void    push(StElem  value);
        StElem  pop();
        void    is_ok();
        void    print();
        void    dump();


};

//------------CONSTRUCTORS----------------
template <typename StElem>
Stack<StElem>::Stack(unsigned length):
    MAX_STACK(10000),
    counter(0),
    len(length),
    data(new StElem[len < MAX_STACK? len : MAX_STACK])
{ }

template <typename StElem>
Stack<StElem>::Stack():
    MAX_STACK(10000),
    len(MAX_STACK),
    data(new StElem[MAX_STACK]),
    counter(0)
{ }

//------------CLASS-MEMBERS----------------
template <typename StElem>
void Stack<StElem>::push(StElem val)
{
    is_ok();

    if (counter == len+1) resize_();

    data[counter++] = val;

    is_ok();

}

template <typename StElem>
StElem Stack<StElem>::pop()
{
    is_ok();

    if (counter == 0)
        throw std::underflow_error("Can't pop! Stack is empty!");

    counter--;

    return data[counter];
}

template <typename StElem>
void Stack<StElem>::is_ok()
{
    if (!data) throw std::bad_alloc();

    if (len > MAX_STACK)  throw std::overflow_error("Too big length of stack!");
    if (!(0 <= counter && counter <= len+1)) throw std::out_of_range("Counter is out of stack!");

}

template <typename StElem>
void Stack<StElem>::print()
{
    is_ok();

    if (counter == 0) std::cout << "Stack is empty.\n";

    for(int i = counter - 1; i >= 0; i--)
        std::cout << "#" << i + 1 << "\t" << data[i] <<"\n";
}

template <typename StElem>
void Stack<StElem>::dump()
{
    std::cout << "-----------------------\n"
    << "STACK[0x" << this << "]\n";

    if(this)
        std::cout << "\tCOUNT = " << counter
        << "\n\tDATA[0x" << data << "], max"
        << len << "\n\n";

    if(data)
        for(int i = 0; i < len; i++)
            std::cout << "\t\t[" << i <<"] = "
            << data[i] << ((i < counter)? "*" : " ")
            <<"\n";

}

template <typename StElem>
int Stack<StElem>::resize_()
{
    is_ok();

    if (len * 2 < MAX_STACK) len *= 2;
    else throw std::overflow_error("Can't resize stack! Stack is too big!");;

    data = (StElem*) realloc(data, len);

    return 0;
}

//------------DESTRUCTOR----------------
template <typename StElem>
Stack<StElem>::~Stack()
{
    delete[] data;

    data = nullptr;

    counter = -1;
}

#endif // STACK_H_INCLUDED
